Screen & Window Management "The possession of gold has ruined fewer men than the lack of it." Thomas Bailey Aldrich, 1903 Introduction To put it bluntly, Turbo Pascal's writeln just isn't powerful enough for today's sophisticated applications. That's why Gold includes GOLDFAST and GOLDWIN. These units provide a powerful set of screen and window management routines, including the following: A family of flexible screen writing procedures making it easy to write at a specific location, write right-justified, write text automatically centered between two coordinates, etc. Routines to draw boxes and lines in a variety of shapes and sizes. The line drawing functions can automatically join-up with other lines already on display. 25, 43 and 50 line displays are supported. Virtual screens which can be created, written to and then moved to the visible screen for viewing. Multiple overlapping windows which can be partially dragged off screen, tiled, zoomed and stretched. Gold can write to partially or fully obscure Windows as well as the underlying background. All or part of the screen can be saved and restored with a variety of special effects. Routines to read the text or attribute directly from a screen or window. Functions to change the cursor shape and position. A set of boilerplate windows to prompt the user to select actions such as OK or Cancel, Yes or No, Live or Die, etc. TTT 5 Users -- the windows management functions have been totally re-written for Gold. The old-style MkWin and RmWin procedures are still supported, but they don't offer any new functionality and are primarily offered for backward compatibility. To really leverage the power of Gold, you should change over to the new Winxxx procedures. Understanding the Active Target If you don't have any windows or virtual screens, life is easy; every time you call one of the screen writing procedures (such as WriteAT) the text is written to the screen for all to see. No problem. However, if you have windows displayed or virtual screens created, Gold has to know where the text should be written. Remember, you can write to any window, virtual screen, or the visible screen. For example, on a desktop application, you might want to write some help text in window 1, display a list in window 2, and perhaps write the current time to the background. Rather than force you to specify the target item with every procedure call, Gold uses the notion of active target. That is, at any one time either the visible screen, a virtual screen, or a window will have the active target, and Gold will direct all screen writing activity to that target. If you write some text which doesn't appear, check the active target -- you might be writing to a virtual screen or an obscured window. The following procedures support target management: ActivateWindow(Win:word); When you create a window, a window number or handle is returned. This procedure sets the target to the specified window. If the specified window does not exist, this procedure will be ignored. ActivateTopWindow; Makes the top-most window the target. If there are no windows, this procedure will be ignored. ActivateVirtualScreen(Page:word); Sets the target to the specified virtual screen. If the virtual screen does not exist, this procedure will be ignored. ActivateVisibleScreen; Sets the target to be the main screen. Gold will ignore all active windows and will write directly to the display. If there are windows displayed, you should use the next function. ActivateBackground; Use this function when windows are displayed and you want to write text behind the windows, i.e. to the background. If no windows are on display, this procedure is ignored. General Purpose String Writing The Write Procedures The following GOLDFAST functions will help you to quickly and easily write wherever you want: WritePlain(X,Y:byte; Str:string); WriteAT(X,Y,FB:byte; Str:string); WriteCol(Col,Row:byte; Str:string); WriteClick(X,Y,FB:byte;Str:string); WriteCenter(Y,FB:byte;Str:string); WriteMiddle(X,FB:byte;Str:string); WriteBetween(X1,X2,Y,FB:byte;Str:string); WriteRight(X,Y,FB:byte;Str:string); WriteVert(X,Y,FB:byte;Str:string); The procedure names are self-explanatory, but you can refer to the Reference Manual or the on-line help for a full description of each procedure. Note that Gold does not move the cursor during screen writes. If you specify a color attribute (FB) of UseTint (defined in GOLDATTR) with any of the screen writing procedures Gold will use the existing display attribute, i.e. written text will assume the color of the underlying screen or window. TTT 5 Users -- all functions which accept a color use a combined color attribute, rather than separate foreground and background attributes. Refer to the section Specifying Display Colors in Chapter 3 for more information. Writing ~Hi~ In many applications you will want to write part of a string in one color, and the remainder of the string in another color. For example, when displaying the text "Are you sure you want to format the network server?", you might want the words format and network server to be in a color which makes them stand out clearly. One way to achieve this would be to write the text using several WriteAt statements, with a different attribute for each statement. A slicker way is to use Gold's WriteHi functions. WriteHi(X,Y,HiFB,FB:byte;Str:string); WriteHiCenter(Y,HiFB,FB:byte;Str:string); By embedding tilde characters ( ~ ) in the string, you can identify which portions of the string are to be written in each attribute. All characters up to the first tilde are written in the FB attribute, the text following the first tilde is written in the HiFB attribute. Thereafter, the attribute switches every time the tilde is encountered. The tilde characters are not displayed. To write the server format warning (described earlier) you might use the following statement: WriteHi(10,13,LightgrayOnBlue,WhiteOnBlue, 'Are you sure you want to ~format~ the ~network server?'); The concept of embedding tilde characters in strings is supported throughout Gold. For example, if you add an item to a pull down menu you can highlight a character by enclosing it within tildes, e.g. '~W~indow'. If you want to write a string with the first capital letter in a different color, use WriteCap as follows: WriteCap(X,Y,FBCap,FB:byte;Str:string); Run the demo DEMFS1 to see a variety of the screen writing functions in action. Erasing Text and Changing Display Colors The following four procedures can be used to clear all or part of the text: Clear(FB:byte; C:Char); Clears the entire screen or window and fills the area with the specified character using the specified color. PartClear(X1,Y1,X2,Y2:byte; FB:byte; C:char); Similar to Clear, but only clears a specified region of the screen or window. ClearLine(Y,FB:integer); Clears an entire line, i.e. fills the entire line with spaces, using the specified attribute. ClearText(X1,Y1,X2,Y2,FB:byte); Similar to Clearline, but a rectangular section of the screen or window is cleared. Writing Non-String Information One of the great strengths of Turbo Pascal's writeln is its ability to accept any number of variables of many different types. Unfortunately, the compiler does not allow third-party developers to create custom procedures with similar flexibility! There is no (practical) way around the fact that Gold's screen writing procedures only support strings. However, if you need to write other types, you can use the string conversion functions in GOLDSTR to cast different types to a string. For example, if you want to write an integer variable to a string you could use the IntToStr function as follows: WritePlain(1,1,IntToStr(ClientsAge)); Although you may only pass one string argument to Gold's screen writing procedures, you can use Turbo Pascal's string concatenation operators to build a single string argument from multiple string components. For example, you could display someone's age with the following concatenated string: WritePlain(1,1,'Your age is '+IntToStr(ClientsAge)); By using string casting functions and concatenation you can gain eighty percent of writeln's flexibility and take advantage of Gold's more powerful string writing procedures. One final thought. If you frequently write non-string types, you might consider writing your own procedure which is passed the non-string type and which, in turn, calls Gold's procedure. For example, the following procedure could be used to write any integer type: procedure WriteNumAt(X,Y,Attr: byte; Val:longint); {} begin WriteAT(X,Y,Attr,IntToStr(Val)); end; { WriteNumAt } You might be wondering why we didn't include a whole family of procedures in Gold to accommodate all the varied types that you might need. We decided that 750 procedures was enough already. Write your own! Box and Line Drawing To draw a box on the screen or window, simply call one of the following procedures: Box(X1,Y1,X2,Y2,FB,style:byte); Draws a rectangular box (using the ASCII box drawing characters) at the specified coordinates in the specified attribute. The style byte is described below. Note that the interior of the box is not written -- whatever was on the screen will remain visible. FBox(X1,Y1,X2,Y2,FB,style:byte); Like Box, but the interior of the box is filled. GrowFBox(X1,Y1,X2,Y2,FB,style:byte); Like FBox, but the box grows on the screen with a cutesy exploding effect. Box3D(X1,Y1,X2,Y2:byte;TLFB,BRFB,Style:byte); Like FBox, but the upper left and lower right sides are displayed with different attributes, giving a chiseled or 3-D effect. (Hey, its not bad for DOS.) The style attribute may have a value in the range 0 to 9. The box styles correspond with the window styles used throughout Gold. The appearance of the box is dependent upon the active character set, i.e. the box styles differ when custom characters are in use. The following table describes the supported box styles. Style Standard Chars Custom Chars 0 No box (spaces) No box (spaces) 1 Single-line Single-line 2 Double-line Single-line 3 No box (spaces) No box (spaces) 4 Single-line Single edge line 5 Two-line menu layout Two-line menu layout 6 Single line on three sides Single line on three sides 7 Single-line chiseled out Single-line chiseled out 8 Single-line chiseled in Single-line chiseled in 9 Special notepad Special notepad Execute the program DEMFS2.PAS to see the supported box styles. To draw a single line using the following two functions, use a Style value of 1 for single lines and 2 for double lines: HorizLine(X1,X2,Y,FB,Style:byte); VertLine(X,Y1,Y2,FB,Style:byte); Use the smart-line drawing functions if you want to join intersecting lines. They will automatically draw box corners, and T junctions to join lines together. Cool. SmartVertLine(X,Y1,Y2,FB,Style:byte); SmartHorizLine(X1,X2,Y,FB,Style:byte); Execute the demo file DEMFS3.PAS to see the smart-line functions in action. Reading the Display This section has nothing to do with optometry. If you want to literally pull characters or attributes from a specific location on the screen or window, you can use the following functions: ReadChar(X,Y:byte):char; Returns the character located at position (X,Y) of the target screen or window. ReadAttr(X,Y:byte):byte; Returns the attribute (or color) at position (X,Y) of the target screen or window. ReadWord(X,Y:byte;var Attr:byte; var Ch: char); Combines the above two functions into a single procedure call. Customizing Scroll Bar Appearance Gold uses scroll bars in windows, lists, forms, etc. If you don't like the default characters used to create the scroll bars (shame on you), you can call the following procedure to instruct Gold to use your preferred characters: SetScrollChars(U,D,L,R,E,B:char); Defines which characters will be used to draw horizontal and vertical scroll bars. See the Reference Guide for further details. If you want to set the characters back to the (superior) Gold defaults, simply call the procedure SetScrollDefaults. Call procedures WriteHScrollBar and WriteVScrollBar to experiment with the scroll bar appearance. See the demo DEMFS4.PAS. Automatic Line Wrapping GOLDFAST defines a global variable LineWrap which is used by the screen and window writing code to control whether Gold automatically wraps long strings to the next line. By default, line wrapping is turned off. If you want enable line wrapping, simply set LineWrap to true as follows: LineWrap := true; Understanding Window Coordinates Normally, Gold uses global screen coordinates (which match the screen dimensions). For example, the (X,Y) coordinates (5,3) identify a location five character positions from the left side of the display and three rows down from the top of the display. Gold also supports local or window coordinates. When a local window is set, all screen writing coordinates become relative to the local window. The following routines provide coordinate support: SetWindow(X1,Y1,X2,Y2: byte); ResetWindow; SetWinIgnore(On:Boolean); GetSetWinIgnore(On:Boolean):boolean; Use the procedure SetWindow to limit screen writing within the specified coordinates. To remove the local window coordinates call ResetWindow -- this sets the screen writing back to the global coordinate system. Even though local window coordinates are set, you can instruct Gold to ignore them (or not) using the SetWinIgnore function. Alternatively, call GetSetWinIgnore if you want to record the ignore state, before temporarily setting it. Having written the appropriate data, set the coordinate state to its previous setting. You might use this function, for example, in a hooked procedure which displays a clock at the top right of the display, as follows: procedure DisplayTime; var IgnoreState: boolean; begin IgnoreState := GetSetWinIgnore(true); WriteAT(70,1,0,Time); SetWinIgnore(IgnoreState); end; { DisplayTime } The principles of active window coordinates apply to screens, virtual screens and windows. The demo program DEMFS5.PAS illustrates how to manipulate the window coordinate settings. Managing Screens Move Data Around the Screen The following three procedures manipulate data already visible on a screen: CopyScreenBlock(X1,Y1,X2,Y2,X,Y:byte); Copies text and attributes from one part of the screen to another. The source and target areas can overlap. The procedure is passed the upper left and lower right coordinates of the area to copy and the upper left coordinates of the target area. MoveScreenBlock(X1,Y1,X2,Y2,X,Y:byte); This procedure is similar to CopyScreenBlock except the original source area is erased. Scroll(Way:gDirection;X1,Y1,X2,Y2:byte); Scrolls text in a rectangular region of the screen. The data can be scrolled in any of the following four directions: Up, Down, Left, Right. One row or column of text is removed, blank characters are inserted in their place. Using Virtual Screens When is a screen not a screen? When it's a virtual screen. A virtual screen emulates the characteristics of the standard video memory, but is located on the heap. Virtual screens are used to save copies of the visible screen, as well as to prepare screens which can be pushed onto the visible screen in a flash (or with a sliding special effect). By default Gold supports five virtual screens. If (for some bizarre reason) you need more virtual screens, change the MaxVirtualScreens setting in GOLDFAST to a higher value. Creating Virtual Screens To create a virtual screen simply call CreateScreen as follows: CreateScreen(Page,W,D,FB:byte); The page number is a value in the range 1 to MaxVirtualScreens (see note above). If that page is being used by another virtual screen, the screen will be overwritten. W and D represent the screen width and depth, respectively. A virtual screen can be as large as 255 characters wide by 255 rows deep. The last parameter, FB, is the default attribute of the newly created screen. Before writing to a virtual screen you should set the target focus using the ActivateVirtualScreen procedure (discussed earlier). Saving the Visible Screen By calling SaveScreen you can create a virtual screen (with the same dimensions as the display) and automatically copy the visible screen contents to the virtual screen along with the cursor position and size. Use this function when you want to save the screen in its entirety to restore it later. SaveScreen(Page:byte); Saves the current screen (on the heap) for a subsequent restore. The only parameter passed is the page or screen number. If a screen has already been created with the same screen number, the old screen will be overwritten. When a virtual screen is created using CreateScreen or SaveScreen, memory must be allocated on the heap. Always check the LastFastError function to make sure the screen creation was successful. If the procedure fails due to a lack of memory, there are two probable causes. 1 -- There really isn't enough memory. 2 -- The maximum heap available has been restricted by the $M compiler directive (or the IDE equivalent). Restoring Screens The user can only see the contents of a virtual screen when it is restored to the visible screen. To quickly restore a screen call the RestoreScreen procedure and identify the page or screen number of the page to be restored. The virtual screen is not affected by a restore; you can restore the same screen many times. If you only want to restore part of a virtual screen, use the following function: PartRestoreScreen(Page,X1,Y1,X2,Y2,X,Y:byte); Restores part of a virtual screen. The procedure is passed the screen number, the upper-left and lower-right coordinates of the area of the screen to be restored and the target coordinates for the top left corner of the area, i.e. part of a saved screen can be restored to a different location on the visible screen. For a little more pizzazz you can use a slide.i..n...g effect when you restore the screen. A little bit of flash goes a long way. The two slide functions (listed below) accept an additional argument of type gDirection, which is declared in GOLDFAST as follows: gDirection = (Up,Down,Left,Right,Vert,Horiz); As you might have deduced (Iowans excepted) this argument controls the direction used to slide the screen onto the display. SlideRestoreScreen(Page:byte;Way:gDirection); PartSlideRestoreScreen(Page:byte;Way:gDirection;X1,Y1,X2,Y2:byte); Disposing of Screens Virtual screens consume memory (about 4000 bytes per 80 by 25 screen). When you no longer need a virtual screen, you should dispose of it using the DisposeScreen function, as follows: DisposeScreen(Page:byte); Dispose of the memory used to store a virtual screen. If the specified screen doesn't exist, the procedure is ignored. Using Condensed Screens Systems equipped with EGA or VGA displays can support the display of 43 or 50 lines of text. GOLDFAST includes the following two procedures to switch between condensed and standard modes. SetCondensed; Changes the display to 43 or 50 line mode. Set25; Sets the display mode to a standard 25 lines. The Gold variable HardVars.Depth is set to reflect the number of display lines. The mouse cursor will not be aware that the size of the screen has been altered. Having switched display modes you should call the mouse function MouseConfine (discussed in the next chapter) to allow the mouse to navigate the entire visible screen (and no more). Run the demo DEMFS6.PAS to see how to set display modes, and how to adjust the mouse. Using Prefabricated Windows Most applications use a set of "standard windows" to interact with the user, e.g. message windows with some text and an OK button. Gold provides a set of prompt procedures and functions which make it easy to display text in a window. In each case, the procedure is passed a title and a message, and Gold computes the appropriate window size and position based on the length and content of the message text. For example, the following statement would instruct Gold to display a window with the title "Yowsa" and the message "What's happening babe?": PromptOK('Yowsa','What''s happening babe?'); The user would be presented with the message window containing the text and a single OK button. Gold automatically calculates the window dimensions based on the input. The window style (i.e. box border) can be modified by setting the value of the variable WinVars.PromptStyle to a value in the range 1 to 9. The default is style 7. Gold provides the following prompt procedures and functions: PromptOK(Tit,Msg:string); Displays a message in a window with an OK button. PromptOKCancel(Tit,Msg:string): byte; Displays the title and message with OK and Cancel buttons. A 1 is returned if OK is selected, and a 2 if Cancel is selected. PromptYesNo(Tit,Msg:string): byte; Like PromptOKCancel but the buttons say Yes and No. PromptCustom(Tit,Msg:string; But1,But2,But3:StrButton; HK1,HK2,HK3, Default:word; WaitTime:longint): byte; Displays a custom prompt window with up to three custom buttons. As well as the title and the message, the function is passed the text of three buttons (null if the button is not needed) along with the hotkey for each button and the default. The final parameter is the wait time in milliseconds -- specify zero if you want to force the user to make a choice, otherwise the function will return a zero if the user has not pressed a button before the wait period has expired. In addition to the above prompt functions which accept the message as a single string, there are a corresponding set of functions which accept string linked lists (discussed in Chapter 13) and are called PromptOKStrSLL, PromptOKCancelStrSLL, PromptYesNoStrLL, and PromptCustomStrLL. These functions allow you to display messages longer than 255 characters. Both the message text and the buttons supported embed "~" characters to allow two display colors to be used. Also, you can embed a split vertical bar character "|" in the message string to force a line break, i.e. all text following the character will be written to the next line. One final tip: any line commencing with the "^" character will be automatically centered in the window. All these special characters are automatically stripped from the message before it is displayed. The button strings used in the standard (non-custom) prompt windows are defined as variables in GOLDWIN as follows: OKButStr: strButton = ' ~O~K '; OKHotKey = 280; { Alt+O } CancelButStr: strButton = '~C~ancel'; CancelHotKey = 302; { Alt+C } YesButStr: strButton = ' ~Y~es '; YesHotKey = 277; {Alt-Y} NoButStr: strButton = ' ~N~o '; NoHotKey = 305; {Alt-N} Both the text and the hotkeys can be changed for international applications. If you need to display a message longer than 255 characters, the message can be passed in the form of a string linked list (discussed in Chapter 13) using the following functions: PromptOKStrLL(Tit:string;StrLL:StringLL); PromptOKCancelStrLL(Tit:string;StrLL:StringLL): byte; PromptYesNoStrLL(Tit:string;StrLL:StringLL): byte; PromptCustomStrLL(Tit:string; StrLL:StringLL; But1,But2, But3:StrButton; HK1,HK2,HK3,Default:word; WaitTime:longint): byte; Other Gold units provide specialized windows such as forms, lists, calendars, calculators, file browsers and text editors. Customizing Display Colors The colors used by the prompt windows are defined in GOLDTINT. The following table lists the member name along with the area affected: Tint Element Description PromptBorder1 The color of the border line. PromptBorder2 The secondary color of the border line when the style variable WinVars.PromptStyle is set to 7 or 8. PromptTitle The color of the title. PromptBody The color of the window body. PromptBodyHi The window of the highlighted text in the window body, i.e. the text after the '~'. PromptButtonNorm Standard color of the button when the button doesn't have focus. PromptButtonNormHot Hi color of the button when the button doesn't have focus. PromptButtonHi Standard color of the button when the button has focus. PromptButtonHiHot Hi color of the button when the button has focus. If you want to use custom display colors, simply change the appropriate element of the TINT color structure using the GoldSetcolor procedure. The following code is an extract of DEMPMT2.PAS: if ColorScreen then begin GoldSetColor(PromptBorder1,LightRedOnRed); GoldSetColor(PromptBorder2,LightRedOnRed); GoldSetColor(PromptTitle,WhiteOnRed); GoldSetColor(PromptBody,LightGrayOnRed); GoldSetColor(PromptButtonHiHot,WhiteOnMagenta); GoldSetColor(PromptButtonHi,YellowOnMagenta); GoldSetColor(PromptButtonNormHot,YellowOnMagenta); GoldSetColor(PromptButtonNorm,LightgrayOnMagenta); end; Run the demos DEMPMT1.PAS to DEMPMT5.PAS to see the prompt windows in action. Creating and Managing Custom Windows If the standard prompt windows (described above) don't meet your needs, you can create your own custom windows. Window Principles Gold supports an infinite number of windows (limited only by memory). A window has very similar properties to a screen: each window has a width, each window has a height, a window has an active cursor position and shape, and within the window there is an active area which can be written to. In addition to the screen-like properties, a window has a position, a style (or border design), and a title. When you want to display some information in a window, you should take the following steps: Decide on the window's dimensions, initial position, and style (discussed in the next section), then create a window structure by calling the WinCreate function, e.g. WinNum := WinCreate(5,5,75,20,4); The value returned by WinCreate is the logical window number (or handle) -- the first window will be window 1, etc. This window handle must be specified in the procedures called in steps 2 and 3. If WinCreate returns a zero, the window was not created (probably due to a lack of memory). Note: Gold has not actually displayed the window at this stage; behind the scenes a window structure has been created in Gold's internal window list. Customize the window characteristics (such as the colors, title, etc.) using the WinSet... functions, e.g. WinSetTitle(1,' The Title '); Display the window by calling WinDisplay, e.g. WinDisplay(1); Get input from the user and check to see whether it is a window-specific keystroke/mouse action by calling the boolean function IsWinKey. If the function returns true, pass the keystroke data to the procedure WinProcessKey for processing, e.g. GetInput; if IsWinKey(LastKey,LastX,LastY) then WinProcessKey(LastKey,LastX,LastY); Refresh the window contents (having written new data to it) by calling WinDrawTop or WinDrawAll. The following code is an extract from DEMWIN1.PAS which illustrates how to create, display and write to a window: WinHandle := WinCreate(5,5,75,20,1); WinSetType(WinHandle,WMove); WinSetTitle(WinHandle,' My First Window '); WinDisplay(WinHandle); WritePlain(1,1,'This .....'); WinDrawAll; MouseShow(true); {now process keystrokes} with KeyVars do repeat GetInput; if IsWinKey(LastKey,LastX,LastY) then WinProcessKey(LastKey,LastX,LastY); until (LastKey = 27) or (LastKey = 600); MouseShow(false); ResetStartupMode; Run the program DEMWIN1.PAS to see the code in action. To summarize, the window management process is: create, customize, display, handle input, redraw. Window Styles A window style controls the format of the window border, i.e. whether it is single line, double line, etc. Gold supports nine different window styles -- ten, if you count style 0 which is no border. When a window is created (with the WinCreate function) the last byte parameter identifies the window style, and it should be a value in the range 0 through 9. These style numbers correspond with the box styles discussed on page 5-6. As with box styles, window styles are affected by the use of custom characters (refer to Chapter 3 for more information). Run the demo DEMWIN2.PAS to interactively modify the window styles and see the impact on the window border. The WinSet... Procedures After a window has been created, the properties of the windows can be changed from the defaults by using the following procedures: WinSetType(Win:integer;W:WinType); The window type controls which of the window border icons are active and visible. Windows can be a combination of closeable, moveable and stretchable. The first parameter identifies which window will have the type changed, and the second parameter indicates the new type. WinType is defined (in GOLDWIN) with the following members: WinType Description WPlain No icons. WClose Close icon. WMove Close icon and window can be dragged. WMoveNoClose Window can be dragged but no close icon. WStretch Window can be stretched, dragged and closed. WinSetMinSize(Win:integer;Width,Depth: byte); Defines the minimum size that a user can make a stretchable window. WinSetScrollType(Win:integer;S:ScrollType); Defines whether the window has horizontal and/or vertical scroll bars. The enumerated type ScrollType is defined in GOLDFAST with the following members: NoScroll, HorizScroll, VertScroll, BothScroll. WinSetColor(Win:integer; A:TintElement;C:byte); Sets the display color of a specific component (or element) of the window, e.g. the icons, the frame, the body, etc. See the section Window Colors (below) for further information. WinSetShowNum(Win:integer;On:boolean); Controls whether or not the window number is displayed on the top right of the window border. WinSetTitle(Win:integer;Tit:string); Sets the window title. Set to null to erase a title. WinSetPosition(Win:integer;NewX,NewY:shortint); Use this procedure to change the position of the top left corner of the window. WinSetStretchProc(Win:integer;S:StretchProc); During a user actioned window stretch (i.e. when the user is dragging the lower right window corner) Gold can call a stretch procedure to provide a way for the application to dynamically update the window during a stretch operation. Call this procedure to identify the name of the procedure which will be called as the window is stretched. Note that the procedure must be declared far with the following format: procedure MyProc(X1,Y1,X2,Y2:byte); Window Shadows GOLDFAST includes the global variables ShadowType and ShadowAttr which control the position and color of window shadows. The shadow attribute may be any value in the range 0 to 255, and has a default value of 7 (lightgray on black). The following table identifies the impact of different ShadowType values: Value Shadow Position 0 Shadows drawn up and to the left. 1 Shadows drawn up and to the right. 2 Shadows drawn down and to the left. 3 Shadows drawn down and to the right. 4 or more No shadow. Window Colors For coloring purposes, a window is composed of several components, each of which may be assigned a different color. The table below lists the component together with a description. Component Description WinBorder The standard window border. WinBorder3DIn The "in" bump portion of the border for styles 7 and 8. WinBorder3DOut The "out" bump portion of the border for styles 7 and 8. WinTitle The window title. WinBody The central part of the window (where text is written). WinIcons The close, zoom and stretch icons. WinCaption The color of the title bar (or caption) when used in styles 3 and 6 (the Windows emulation styles). WinCustom The color used to draw the Microsoft Windows-style close and zoom icons for window styles 3 and 6. WinBorderOff The colors used to draw windows on the desktop which don't have focus. There are two additional window colors which were not included in the table: WinMoveHi and WinMoveBody. These members are used to define the colors of the message displayed when a user presses Ctrl-F5 to move a window using the cursor keys. The default window colors are defined in GOLDTINT; there is one set for monochrome systems and another for color systems. To change the display colors for an individual window, you can call the WinSetColor procedure (discussed earlier) identifying the window component and the desired color, e.g. WinSetColor(WinNum,WinBorder,WhiteOnRed); WinSetColor(WinNum,WinTitle,YellowOnRed); WinSetColor(WinNum,WinBody,LightgrayOnRed); WinSetColor(WinNum,WinIcons,LightcyanOnRed); However, if you want to use a different (non-default) color scheme for all windows, change the GOLDTINT defaults prior to creating the windows. Refer to the section Customizing Display Colors in Chapter 3 for more information. The Impact of Custom Characters Gold creates custom characters for line drawing, window icons, etc. The standard ASCII double line characters are not available when custom characters are active. If you have custom characters enabled, and you request a window style which uses double-line characters (such as style 2) Gold will automatically draw single lines instead. You can see the effect of the custom characters on window shapes by running DEMWIN1.PAS and toggling the Custom Characters check box. Refer to Chapter 3 for more information on the impact of custom characters on box and border drawing. Processing Window Input There are many different user inputs which must be managed in a custom window: clicking on the various icons, dragging the window around the screen, stretching the window, double-clicking on the title, etc. Fortunately, Gold can manage all these inputs and leave you free to deal with the non-window related keystrokes and mouse clicks. The intricacies of managing mouse and keyboard input are discussed in the next Chapter, but the principles are simple. Any single user input can be defined with three variables: the keystroke which is of type word, and two bytes which identify the X and Y coordinates of the mouse cursor. Having garnered user input you can test whether the input is a window action by calling the function IsWinKey which is defined as follows: IsWinKey(K:word;KX,KY:byte):boolean; The function is passed three parameters identifying the keystroke details and returns true if the keystroke is a window management keystroke which can be handled automatically by Gold. If the function returns false, the keystroke is not associated with a generic window manipulation task (such as zoom or move), and should be handled by the application's own keyboard handler. If IsWinKey returns true, call the function WinProcessKey (defined below) to have Gold process the keystroke and manipulate the window as necessary. WinProcessKey(var K:word; var KX,KY:byte); This procedure is passed the window-related keystroke. Having processed the keystroke, Gold updates the passed parameters to indicate what window activity transpired. An application should evaluate the updated parameters and redraw the window contents as appropriate. For example, if the window was stretched, K will be updated with a value of 602, and the values of X and Y are irrelevant. The following table identifies the values which can be assigned to K along with those instances where X and Y also have a meaning. Key Explanation 600 The user clicked on the close icon and the window has been closed. 601 The window was moved or dragged. 602 The window was zoomed or stretched, i.e. resized. 610 The user clicked on the up arrow on the vertical scrollbar. 611 The user clicked on the down arrow on the vertical scrollbar. 612 The user clicked on the left arrow on the horizontal scrollbar. 613 The user clicked on the right arrow on the horizontal scrollbar. 614 The user clicked along the length of the vertical scrollbar. X is updated to reflect the number of characters down the scrollbar where the user clicked, and Y identifies the length of the scrollbar in characters. 615 The user clicked along the length of the horizontal scrollbar. X is updated to reflect the number of characters along the scrollbar where the user clicked, and Y identifies the length of the scrollbar in characters. The Timing of Window Updates The code which manages the window kernel is complex and primarily written in assembly language. So there! However, an understanding of the general architecture will help you to figure out when the visible screen is updated. It may also explain some of the odd behavior that occurs when you are tracing a program using the debugger. To allow for updates to fully or partially obscured windows, Gold maintains a window image off-screen. More often than not, an application will write several different strings to a window at one time. When you write to a window, the off-screen image of the window is immediately updated, but, to avoid unnecessary screen updates, the window image is not immediately transferred to the visible screen. The windows are updated on the visible screen when one of the following two functions is called: WinDrawAll; Forces an update of the screen background and all the windows. Thanks to the brilliance of the windows code, the user will not experience any flicker as the windows are updated. WinDrawTop; The top-most window is redrawn. If you want every window update to be automatically displayed on the visible screen, set the global GOLDFAST variable ShowNow to true. Cursor Management Routines are available for hiding and displaying the cursor as well as changing the cursor shape from thin to half to full, or to any other available shape. The cursor routines function on monochrome and color video systems. As with screen and window updates, the cursor management functions are specific to the active window or screen. Setting the Cursor Shape The following procedures can be used to set the cursor to one of four standard shapes: CursorOff; Makes the cursor invisible. CursorOn; Makes the cursor visible and sets it to the default DOS shape, i.e. a blinking line at the bottom of the character. CursorHalf; Makes the cursor a block filling the lower half of the character field. CursorFull; Makes the cursor a full block filling the entire character field. In text mode, an individual character is comprised of a number of scan lines. Usually the cursor is located on one or two scan lines near the bottom of the character. Different display systems use varying numbers of scan lines. For example, a monochrome display usually has fourteen scan lines, as does an EGA system, but a CGA only has 8. Use the function CharHeight to return the actual number of scan lines per character, before using CursorSize to set the cursor to a custom size. CharHeight: integer; Returns the number of scan lines per character used by the video display system in the current mode. CursorSize(T,B:byte); Sets the cursor to the specified top and bottom scan lines. The top scan line (e.g. the top bar of the letter "T") is scan line zero. As an example, to set the cursor to a half block filling the upper half of the screen (on a VGA system), call: SizeCursor(0,7); DEMFS8.PAS illustrates the cursor setting procedures. Setting the Cursor Position You guessed it, to change the position of the cursor, call GotoXY and pass the new (X,Y) coordinates of the cursor. Gold's GotoXY is more sophisticated than Borland's GotoXY. For example, if you position the cursor in a window in a location that is physically off screen (because the window has been partially dragged screen), Gold will automatically make the cursor invisible. GotoXY(X,Y:byte); Positions the cursor (using local coordinates) relative to the active window or screen. Getting the Cursor Details The following three routines will return information about the active window or screen's cursor location and size: CursorFind(var X,Y,Top,Bot:byte); Updates the passed parameters with the (X,Y) coordinates of the cursor, and the cursor's top and bottom scan lines. WhereX: byte; Returns the X coordinate position of the cursor. WhereY: byte; Returns the Y coordinate position of the cursor. If you want to manage the cursor using standard global screen coordinates (ignoring any local coordinates, windows and screens, etc.), you can use one of the following absolute cursor routines: AbsGotoXY(X,Y:byte); AbsWhereXY(var X,Y:byte); AbsCursorSize(T,B:byte); Error Management Some of the GOLDFAST and GOLDWIN functions have the potential to fail. For example, CreateScreen may have insufficient memory. The Reference Manual identifies every procedure and function which may fail and set a non-zero error code. GOLDFAST and GOLDWIN provide the following two functions to test the success of the last command: LastFastError: integer; LastWinError: integer; Refer to Chapter 3 for more information on error management. Other Neat Stuff Listed below are some additional procedures and functions which you may find useful: DrawShadow(X1,Y1,X2,Y2:integer); Draws a lightgray shadow down and to the right of the specified coordinates. Attrib(X1,Y1,X2,Y2,FB:byte); Changes the display attribute (i.e. color) of a region of the active screen or window. Note the text in the affected area is not deleted -- only the color of the text is changed. The procedure is passed the upper left and lower right (X,Y) coordinates together with the desired display attribute. ResetStartUpMode; Sets the display device to the same mode that was in effect when the program was executed and automatically removes any custom characters and replaces them with the standard ASCII character set.